package com.appistry.storage.domain.impl;

import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.appistry.api.rest.domain.RestResponse;
import com.appistry.storage.File;
import com.appistry.storage.FileMetaData;
import com.appistry.storage.domain.AbstractRestDomainObject;
import com.appistry.storage.exception.StorageManagerException;
import com.appistry.storage.rest.RestStorageManager;
import com.appistry.storage.util.JsonTransformUtil;

public class FileImpl extends AbstractRestDomainObject implements File {
	private static final Pattern PATH_PATTERN = Pattern.compile(".*/");

	private RestStorageManager manager;
	private String path;
	private FileMetaData cachedFileMetaData;

	public FileImpl(RestStorageManager manager, String path, FileMetaData fileMetaData) {
		this(manager, path);
		this.cachedFileMetaData = fileMetaData;
	}
	
	public FileImpl(RestStorageManager manager, String path) {
		this.manager = manager;
		if (path.charAt(0) != '/') {
			this.path = "/" + path;
		} else {
			this.path = path;
		}
	}
	
	public FileMetaData getFileMetaData() throws StorageManagerException { 	
		if ( cachedFileMetaData == null ){
			return refreshFileMetaData();
		}
		return cachedFileMetaData;
	}
		
	public FileMetaData refreshFileMetaData() throws StorageManagerException {
		try {
			RestResponse response = manager.getFileMetadata(path);
			checkForErrors(response);

			Map<String,FileMetaData> workerFileMetaDataMap = JsonTransformUtil.toWorkerFileMetaDataMap(response.getBodyAsString());
			if(workerFileMetaDataMap==null || workerFileMetaDataMap.size() < 0){
				throw new RuntimeException("There is no metadata for file: " + getPath());
			}
			
			this.cachedFileMetaData = workerFileMetaDataMap.values().iterator().next();
			
			return cachedFileMetaData;

		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public List<String> getIps() throws StorageManagerException {
		try {
			RestResponse response = manager.getFileLocation(path);
			checkForErrors(response);
			
			List<String> ips = JsonTransformUtil.toFileIps(response.getBodyAsString());
			
			return ips;
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public List<String> getLocations() throws StorageManagerException {
		try {
			RestResponse response = manager.getFileLocation(path);
			checkForErrors(response);

			List<String> locations = JsonTransformUtil.toFileLocations(response.getBodyAsString());
			
			return locations;
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public String getName() {
		String name = path;
		if(path.equals("/")){
			return path;
		}

		// Remove trailing '/'
		if (this.isDir()) {
			name = name.substring(0, name.length() - 1);
		}

		Matcher m = PATH_PATTERN.matcher(name);
		if (m.find()) {
			return name.substring(m.end());
		}

		throw new RuntimeException("Unable to parse path: " + path);
	}

	public String getParent() {		
		String name = path;
		if(path.equals("/")){
			return path;
		}

		// Remove trailing '/'
		if (this.isDir()) {
			name = name.substring(0, name.length() - 1);
		}
		
		Matcher m = PATH_PATTERN.matcher(name);
		if (m.find()) {

			// Incase the path is "/file"
			int start = m.start();
			int end = m.end() - 1;
			if (end == start) {
				return "/";
			}

			return name.substring(start, end);
		}

		throw new RuntimeException("Unable to parse path: " + path);
	}

	public String getPath() {
		return path;
	}

	public void deleteFile() throws StorageManagerException {
		try {
			RestResponse response = manager.deleteFile(path);
			checkForErrors(response);
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public InputStream getFile() throws StorageManagerException {
		try {
			RestResponse response = manager.getFile(path);
			checkForErrors(response);

			return response.getBodyAsStream();
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public InputStream getFile(int minReadCopies) throws StorageManagerException {
		try {
			RestResponse response = manager.getFile(path, minReadCopies);
			checkForErrors(response);

			return response.getBodyAsStream();
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public InputStream getFile(String version) throws StorageManagerException {
		try {
			RestResponse response = manager.getFile(path, version);
			checkForErrors(response);

			return response.getBodyAsStream();
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public InputStream getFile(int minReadCopies, long offset, long length) throws StorageManagerException {
		try {
			RestResponse response = manager.getFile(path, minReadCopies, offset, length);
			checkForErrors(response);

			return response.getBodyAsStream();
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public InputStream getFile(long offset, long length) throws StorageManagerException {
		try {
			RestResponse response = manager.getFile(path, offset, length);
			checkForErrors(response);

			return response.getBodyAsStream();
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public InputStream getFile(String version, int minReadCopies) throws StorageManagerException {
		try {
			RestResponse response = manager.getFile(path, version, minReadCopies);
			checkForErrors(response);

			return response.getBodyAsStream();
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public InputStream getFile(String version, int minReadCopies, long offset, long length) throws StorageManagerException {
		try {
			RestResponse response = manager.getFile(path, version, minReadCopies, offset, length);
			checkForErrors(response);

			return response.getBodyAsStream();
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public void moveFile(String fileNameDest) throws StorageManagerException {
		try {
			// InputStream content = new ByteArrayInputStream(fileNameDest.toString().getBytes());
			RestResponse response = manager.moveFile(path, fileNameDest);
			checkForErrors(response);
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public void putFile(InputStream content) throws StorageManagerException {
		try {
			RestResponse response = manager.putFile(path, content);
			checkForErrors(response);
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}

	}

	public void putFile(InputStream content, int copies, int minCopies, int minReadCopies) throws StorageManagerException {
		try {
			RestResponse response = manager.putFile(path, content, copies, minCopies, minReadCopies);
			checkForErrors(response);
		}catch (StorageManagerException e) {
			throw e;
		} catch (Exception e) {
			throw new StorageManagerException(e);
		}
	}

	public boolean isDir() {
		if (path.charAt(path.length() - 1) == '/') {
			return true;
		}
		return false;
	}

	@Override
	public String toString() {
		return path;
	}
}
